﻿#region Copyright 
/*-------------------------------------------------------------------------
* 命名空间名称/文件名:    SSPivot.HangFire/ServiceCollectionExtensions 
* CLR版本:     4.0.30319.42000
* 机器名称:    DESKTOP-NHMLO8A
* 功 能：       N/A
* 类 名：       ServiceCollectionExtensions
* 创建时间：  2025/5/23 16:32:44
* 版本号：      v1.0
* 创建人:        xulong
*-------------------------------------------------------------------------*/
#endregion
using Hangfire.Dashboard;
using Hangfire;
using Microsoft.AspNetCore.Builder;
using SSPivot.Common.Helpers;
using SSPivot.HangFire;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Hangfire.SqlServer;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class ServiceCollectionExtensions
    {
        #region 无用
        //public static void AddHangFireSetup(this IServiceCollection services, IConfiguration configuration)
        //{
        //    if (services == null) throw new ArgumentNullException(nameof(services));
        //    var hfConfig = configuration.GetSection("Hangfire").Get<HangfireConfig>();
        //    //注册Hangfire定时任务
        //    var isEnabledRedis = AppSettingsConstVars.RedisUseTimedTask;
        //    if (isEnabledRedis)
        //    {
        //        var configuration = ConfigurationOptions.Parse(AppSettingsConstVars.RedisConfigConnectionString, true);
        //        _redis = ConnectionMultiplexer.Connect(configuration);
        //        var db = _redis.GetDatabase();

        //        services.AddHangfire(x => x.UseRedisStorage(_redis, new RedisStorageOptions()
        //        {
        //            Db = db.Database, //建议根据
        //            SucceededListSize = 500,//后续列表中的最大可见后台作业，以防止它无限增长。
        //            DeletedListSize = 500,//删除列表中的最大可见后台作业，以防止其无限增长。
        //            InvisibilityTimeout = TimeSpan.FromMinutes(30),
        //        }));
        //    }
        //    else
        //    {
        //        string dbTypeString = AppSettingsConstVars.DbDbType;
        //        if (dbTypeString == DbType.MySql.ToString())
        //        {
        //            services.AddHangfire(x => x.UseStorage(new MySqlStorage(AppSettingsConstVars.DbSqlConnection, new MySqlStorageOptions
        //            {
        //                TransactionIsolationLevel = IsolationLevel.ReadCommitted, // 事务隔离级别。默认是读取已提交。
        //                QueuePollInterval = TimeSpan.FromSeconds(15),             //- 作业队列轮询间隔。默认值为15秒。
        //                JobExpirationCheckInterval = TimeSpan.FromHours(1),       //- 作业到期检查间隔（管理过期记录）。默认值为1小时。
        //                CountersAggregateInterval = TimeSpan.FromMinutes(5),      //- 聚合计数器的间隔。默认为5分钟。
        //                PrepareSchemaIfNecessary = true,                          //- 如果设置为true，则创建数据库表。默认是true。
        //                DashboardJobListLimit = 500,                            //- 仪表板作业列表限制。默认值为50000。
        //                TransactionTimeout = TimeSpan.FromMinutes(1),             //- 交易超时。默认为1分钟。
        //                TablesPrefix = "Hangfire"                                  //- 数据库中表的前缀。默认为none
        //            })));
        //        }
        //        else if (dbTypeString == DbType.SqlServer.ToString())
        //        {
        //            services.AddHangfire(x => x.UseSqlServerStorage(AppSettingsConstVars.DbSqlConnection, new SqlServerStorageOptions()
        //            {
        //                QueuePollInterval = TimeSpan.FromSeconds(15),             //- 作业队列轮询间隔。默认值为15秒。
        //                JobExpirationCheckInterval = TimeSpan.FromHours(1),       //- 作业到期检查间隔（管理过期记录）。默认值为1小时。
        //                CountersAggregateInterval = TimeSpan.FromMinutes(5),      //- 聚合计数器的间隔。默认为5分钟。
        //                PrepareSchemaIfNecessary = true,                          //- 如果设置为true，则创建数据库表。默认是true。
        //                DashboardJobListLimit = 500,                            //- 仪表板作业列表限制。默认值为50000。
        //                TransactionTimeout = TimeSpan.FromMinutes(1),             //- 交易超时。默认为1分钟。
        //            }));
        //        }
        //    }

        //    services.AddHangfireServer(options =>
        //    {
        //        options.Queues = new[] { GlobalEnumVars.HangFireQueuesConfig.@default.ToString(), GlobalEnumVars.HangFireQueuesConfig.apis.ToString(), GlobalEnumVars.HangFireQueuesConfig.web.ToString(), GlobalEnumVars.HangFireQueuesConfig.recurring.ToString() };
        //        options.ServerTimeout = TimeSpan.FromMinutes(4);
        //        options.SchedulePollingInterval = TimeSpan.FromSeconds(15);//秒级任务需要配置短点，一般任务可以配置默认时间，默认15秒
        //        options.ShutdownTimeout = TimeSpan.FromMinutes(5); //超时时间
        //        options.WorkerCount = Math.Max(Environment.ProcessorCount, 20); //工作线程数，当前允许的最大线程，默认20
        //    });

        //}
        #endregion

        #region 说明
        //public class AutoCancelOrderJob
        //{
        //    private readonly ICoreCmsOrderServices _orderServices;

        //    public AutoCancelOrderJob(ICoreCmsOrderServices orderServices)
        //    {
        //        _orderServices = orderServices;
        //    }

        //    public async System.Threading.Tasks.Task Execute()
        //    {
        //        await _orderServices.AutoCancelOrder();
        //    }
        //}
        //自动取消订单任务
        //RecurringJob.AddOrUpdate<AutoCancelOrderJob>(s => s.Execute(), "0 0/5 * * * ? ", TimeZoneInfo.Local); // 每5分钟取消一次订单


        //Fire - And - forget（发布 / 订阅）
        //这是一个主要的后台任务类型，持久化消息队列会去处理这个任务。当你创建了一个发布 / 订阅任务，该任务会被保存到默认队列里面（默认队列是"Default"，但是支持使用多队列）。多个专注的工作者（Worker）会监听这个队列，并且从中获取任务并且完成任务。
        //BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget"));

        //延迟
        //如果想要延迟某些任务的执行，可以是用以下任务。在给定延迟时间后，任务会被排入队列，并且和发布 / 订阅任务一样执行。
        //BackgroundJob.Schedule(() => Console.WriteLine("Delayed"), TimeSpan.FromDays(1));

        //循环
        //按照周期性（小时，天等）来调用方法，请使用RecurringJob类。在复杂的场景，您可以使用CRON表达式指定计划时间来处理任务。
        //RecurringJob.AddOrUpdate(() => Console.WriteLine("Daily Job"), Cron.Daily);

        //连续
        //连续性允许您通过将多个后台任务链接在一起来定义复杂的工作流。
        //var id = BackgroundJob.Enqueue(() => Console.WriteLine("Hello, "));
        //BackgroundJob.ContinueWith(id, () => Console.WriteLine("world!"));

        //这里呢就是需要触发的方法 "0/10 * * * * ? " 可以自行搜索cron表达式 代表循环的规律很简单
        //CancelOrderJob代表你要触发的类 Execute代表你要触发的方法
        #endregion


        public static IServiceCollection AddSSPivotHangfire(this IServiceCollection services, IConfiguration configuration)
        {
            var hfConfig = configuration.GetSection("Hangfire").Get<HangfireConfig>();// ConfigHelper.GetOptions<HangfireConfig>("Hangfire");
            if (!hfConfig.IsEnabled) return services;
            services.AddHangfire(config =>
            {
                //此方法 只初次创建数据库使用即可
                config.SetDataCompatibilityLevel(CompatibilityLevel.Version_170);
                config.UseSimpleAssemblyNameTypeSerializer();
                config.UseRecommendedSerializerSettings();
                var sqlOptions = new SqlServerStorageOptions();
                config.UseSqlServerStorage(hfConfig?.ConnName, new SqlServerStorageOptions()
                {
                    UseRecommendedIsolationLevel = true, //使用推荐隔离级别
                    QueuePollInterval = TimeSpan.FromSeconds(15), //- 作业队列轮询间隔。默认值为15秒。
                    JobExpirationCheckInterval = TimeSpan.FromMinutes(20),//作业过期时间
                    CountersAggregateInterval = TimeSpan.FromMinutes(5),
                    PrepareSchemaIfNecessary = true, // 如果设置为true，则创建数据库表。默认是true
                    DashboardJobListLimit = 50000,//仪表板作业列表限制
                    TransactionTimeout = TimeSpan.FromMinutes(1),//事务超时时间
                    DisableGlobalLocks = false,//禁用全局锁
                });

            });
            services.AddHangfireServer();
            //services.AddScoped<IBackgroundJobManager, BackgroundJobManager>(); 
            //services.AddHangfireServer(options =>
            //{
            //    options.ServerName = "task";//服务名
            //    //在 Enqueue 方法中，可以通过传递第二个参数 queueName 来指定要将作业添加到哪个队列中。例如，以下代码将发送电子邮件任务添加到名为 "email" 的队列
            //    //BackgroundJob.Enqueue(() => SendEmail("hello@example.com"), "email");
            //    //options.Queues = new string[] {  "queue1", "queue2" };
            //    options.SchedulePollingInterval = TimeSpan.FromSeconds(30);
            //    options.WorkerCount = 5;//并行数
            //});
            #region 创建任务
            {
                //var client = new BackgroundJobClient();
                ////创建立即执行任务
                //client.Enqueue(() => Console.WriteLine("Easy!"));
                //BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget"));
                ////创建延时执行任务
                //BackgroundJob.Schedule(() => Console.WriteLine("Hello, world"), TimeSpan.FromDays(1));
            }
            {
                //BackgroundJob 类允许您使用静态方法创建任务。
                //创建立即执行任务
                //BackgroundJob.Enqueue(() => Console.WriteLine("Hello!"));
                ////创建延时执行任务
                //BackgroundJob.Schedule(() => Console.WriteLine("Reliable!"), TimeSpan.FromDays(1));
            }
            #endregion

            #region 周期任务
            {
                //要按照周期性（小时，每天等）调用方法，请使用 RecurringJob 类调用。您可以使用 CRON 表达式 来处理更复杂的使用场景。
                //请使用带有显式recurring JobId参数和Recurring JobOptions的重载.
                //RecurringJob.AddOrUpdate("task1", () => Console.WriteLine("Daily Job"), Cron.Daily);
            }
            {
                //继续任务:
                //Continuations 允许您通过将多个后台任务结合起来定义复杂的工作流。
                //var id = BackgroundJob.Enqueue(() => Console.WriteLine("Hello, "));
                //BackgroundJob.ContinueJobWith(id, () => Console.WriteLine("world!"));
                //RecurringJob类是 RecurringJobManager 类的一个入口。如果您想要更多的权力和责任，请考虑使用它
            }
            #endregion
            #region 操作周期任务
            {
                ////如果存在就删除周期任务
                //RecurringJob.RemoveIfExists("some-id");
                ////触发周期任务
                //RecurringJob.Trigger("some-id");

            }
            #endregion
            #region 依赖注入
            //Job是通过JobActivator创建，如果job依赖其他服务，需要重写JobActivator
            {
                ///// <summary>
                ///// Hangfire Job依赖注入
                ///// </summary>
                //public class JobActivator : Hangfire.JobActivator
                //        {
                //            private IServiceProvider _serviceProvider;

                //            public JobActivator(IServiceProvider serviceProvider)
                //            {
                //                _serviceProvider = serviceProvider;
                //            }

                //            public override object ActivateJob(Type type)
                //            {
                //                using var scope = _serviceProvider.CreateScope();
                //                return scope.ServiceProvider.GetService(type);
                //            }
                //        }


                //然后在启动Hangfire服务器之前，将其注册:
                //  // Somewhere in bootstrap logic, for example in the Global.asax.cs file
                //   var container = new Container();
                //    GlobalConfiguration.Configuration.UseActivator(new ContainerJobActivator(container));
                //    ...
                //app.UseHangfireServer();

            }
            {
                //autofac注入
                //GlobalConfiguration.Configuration.UseAutofacActivator(builder.Build());
                //租户
                //GlobalConfiguration.Configuration.UseAutofacActivator(builder.Build(), (builder, context) =>
                //{
                //    var tenantId = context.GetJobParameter<string>("TenantId", allowStale: true);
                //    builder.Register<MultiTenantDatabase>(provider => new MultiTenantDatabase(tenantId));
                //});
            }

            #endregion

            #region 过滤器
            //任务可以像ASP.NET MVC操作过滤器一样被拦截。有的时候我们希望记录Job运行生命周期内的所有事件，可以使用过滤器实现
            {
                //            public class LogEverythingAttribute : JobFilterAttribute,
                //IClientFilter, IServerFilter, IElectStateFilter, IApplyStateFilter
                //    {
                //        private static readonly ILog Logger = LogProvider.GetCurrentClassLogger();

                //        public void OnCreating(CreatingContext context)
                //        {
                //            Logger.InfoFormat("Creating a job based on method `{0}`...", context.Job.Method.Name);
                //        }

                //        public void OnCreated(CreatedContext context)
                //        {
                //            Logger.InfoFormat(
                //                "Job that is based on method `{0}` has been created with id `{1}`",
                //                context.Job.Method.Name,
                //                context.BackgroundJob?.Id);
                //        }

                //        public void OnPerforming(PerformingContext context)
                //        {
                //            Logger.InfoFormat("Starting to perform job `{0}`", context.BackgroundJob.Id);
                //        }

                //        public void OnPerformed(PerformedContext context)
                //        {
                //            Logger.InfoFormat("Job `{0}` has been performed", context.BackgroundJob.Id);
                //        }

                //        public void OnStateElection(ElectStateContext context)
                //        {
                //            var failedState = context.CandidateState as FailedState;
                //            if (failedState != null)
                //            {
                //                Logger.WarnFormat(
                //                    "Job `{0}` has been failed due to an exception `{1}`",
                //                    context.BackgroundJob.Id,
                //                    failedState.Exception);
                //            }
                //        }

                //        public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
                //        {
                //            Logger.InfoFormat(
                //                "Job `{0}` state was changed from `{1}` to `{2}`",
                //                context.BackgroundJob.Id,
                //                context.OldStateName,
                //                context.NewState.Name);
                //        }

                //        public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
                //        {
                //            Logger.InfoFormat(
                //                "Job `{0}` state `{1}` was unapplied.",
                //                context.BackgroundJob.Id,
                //                context.OldStateName);
                //        }
                //    }

            }
            //像ASP.NET过滤器一样，您可以在方法，类和全局上应用过滤器
            {
                //        [LogEverything]
                //        public class EmailService
                //{
                //    [LogEverything]
                //    public static void Send() { }
                //}
                ////全局注册
                //GlobalJobFilters.Filters.Add(new LogEverythingAttribute());
            }
            #endregion

            #region 取消
            {
                //取消令牌通过 IJobCancellationToken 的接口暴露出来。
                //当发起取消任务请求时，它通过 ThrowIfCancellationRequested 方法来抛出 OperationCanceledException :
                //public void LongRunningMethod(IJobCancellationToken cancellationToken)
                //{
                //    for (var i = 0; i < Int32.MaxValue; i++)
                //    {
                //        cancellationToken.ThrowIfCancellationRequested();

                //        Thread.Sleep(TimeSpan.FromSeconds(1));
                //    }
                //}

                //当您要将这种调用方法作为后台任务入队时，您可以将 null 值作为token参数的参数传递，
                //或者使用 JobCancellationToken.Null 属性
                //BackgroundJob.Enqueue(() => LongRunningMethod(JobCancellationToken.Null));
            }

            #endregion

            return services;
        }
        public static void UseSSPivotHangfireDashboard(this WebApplication app, IConfiguration configuration)
        {
            var hfConfig = configuration.GetSection("Hangfire").Get<HangfireConfig>();// ConfigHelper.GetOptions<HangfireConfig>("Hangfire");
            if (!hfConfig.IsEnabled) return ;
            app.UseHangfireDashboard(options: new DashboardOptions
            {
                Authorization = new[] { new SSPivotDashboardAuthorizationFilter() },
            });
        }

        #region httpjob.agent
        //public static IServiceCollection AddLHHTHttpJobAgent(this IServiceCollection services)
        //{
        //    services.AddHangfireHttpJobAgent();
        //    return services;
        //}

        //public static void UseLHHTHttpJobAgent(this WebApplication app)
        //{
        //    app.UseHangfireHttpJobAgent();
        //}
        #endregion
    }
}
